#
#   api_geocode.py
#
#   David Janes
#   2008.11.22
#
#	Copyright 2008, 2009 David Janes
#
#	Interface to Google AJAX API
#

import sys
import os
import urllib
import types
import pprint
import types
import socket
import mimetypes

try:
	import json
except:
	import simplejson as json

import bm_extract
import bm_uri
import api
import bm_work
import bm_cfg
import vcard
import mfdict
import hcard

from bm_log import Log

google_keys_core = [ 
	"visibleUrl", 
	"addressLines", 
	"accuracy", 
	"listingType", 
	"GsearchResultClass", 
	"titleFormatted", 
	"viewportmode",
	"publisher",
	"relatedStories",
	"imageId",
	"contentNoFormatting",
]

class Google(api.APIReader):
	"""
	See: http://code.google.com/apis/ajaxsearch/documentation/reference.html#_intro_fonje
	"""

	_item_path = "responseData.results"
	_meta_path = "responseData.cursor"
	_convert2work = bm_work.JSON2WORK()
	_required_attributes = [ "q" ]
	_page_max = -1
	_google_keys = google_keys_core

	_atom_item = {
		"titleFormatted" : "title", 
		"title" : "titleNoFormatting", 
	}

	def __init__(self, **ad):
		ad['v'] = "1.0"
		ad['rsz'] = "large"
		ad.setdefault('_http_referer', 'http://%s' % socket.gethostname())

		api.APIReader.__init__(self, **ad)

	def CustomizePageURI(self, page_index):
		if page_index > 1:
			return	"start=%s" % ( page_index * 8 )

	def CustomizeAtomItem(self, d):
		d = api.APIReader.CustomizeAtomItem(self, d)

		googled = {}
		for key in self._google_keys:
			try: googled[key] = d.pop(key)
			except KeyError: pass

		if googled:
			d["gsearch:gsearch"] = googled

		link = d.get("unescapedUrl") or d.get("url")
		if link:
			if link.startswith("http%3A"):
				link = urllib.unquote(link)

			d["link"] = link

		for key in [ "unescapedUrl", "url", ]:
			try: del d[key]
			except: pass

		try:
			href = d.pop("blogUrl")
			if href:
				api.add_link(d, href, rel = "home", type = "text/html", title = "Blog Home")
		except KeyError:
			pass

		try:
			href = d.pop("ddUrl")
			if href:
				api.add_link(d, href, rel = "alternate", type = "text/html", title = "Driving directions")
		except KeyError:
			pass

		try:
			href = d.pop("ddUrlToHere")
			if href:
				api.add_link(d, href, rel = "alternate", type = "text/html", title = "Driving directions to here")
		except KeyError:
			pass

		try:
			href = d.pop("ddUromHere")
			if href:
				api.add_link(d, href, rel = "alternate", type = "text/html", title = "Driving directions from here")
		except KeyError:
			pass

		try:
			href = d.pop("cacheUrl")
			if href:
				api.add_link(d, href, rel = "alternate", type = "text/html", title = "Google Cache")
		except KeyError:
			pass

		try:
			href = d.pop("signedRedirectUrl")
			if href:
				api.add_link(d, href, rel = "alternate", type = "text/html", title = "Google news signed redirect page")
		except KeyError:
			pass

		try:
			href = d.pop("clusterUrl")
			if href:
				api.add_link(d, href, rel = "alternate", type = "text/html", title = "Google news cluster")
		except KeyError:
			pass

		try:
			href = d.pop("originalContextUrl")
			if href:
				api.add_link(d, href, rel = "via", type = "text/html", title = "Original Context")
		except KeyError:
			pass

		try:
			value = d.pop("published")
			if value:
				value = bm_extract.coerce_datetime(value, otherwise = None, atom = True)

			if value:
				d["published"] = value
		except KeyError:
			pass

		try:
			value = d.pop("publishedDate")
			if value:
				value = bm_extract.coerce_datetime(value, otherwise = None, atom = True)

			if value:
				d["published"] = value
		except KeyError:
			pass

		return	d

class WebSearch(Google):
	_uri_base = "http://ajax.googleapis.com/ajax/services/search/web"

class LocalSearch(Google):
	_uri_base = "http://ajax.googleapis.com/ajax/services/search/local"

	def CustomizeAtomItem(self, d):
		d = Google.CustomizeAtomItem(self, d)
		
		#
		#	Build a hCard from the data
		#	... should add lat/lon here?
		#
		hd = mfdict.mfdict()
		for k_from, k_to in [
			( "country", "%s.%s.%s" % ( vcard.Work, vcard.ADR, vcard.CountryName, ), ),
			( "streetAddress", "%s.%s.%s" % ( vcard.Work, vcard.ADR, vcard.StreetAddress, ), ),
			( "city", "%s.%s.%s" % ( vcard.Work, vcard.ADR, vcard.Locality, ), ),
			( "region", "%s.%s.%s" % ( vcard.Work, vcard.ADR, vcard.Region, ), ),
			( "staticMapUrl", "%s" % ( vcard.Photo, ), ),
			( "title", vcard.OrganizationName, ),
			( "lat", "%s.%s" % ( vcard.GEO, vcard.Latitude, ), ),
			( "lng", "%s.%s" % ( vcard.GEO, vcard.Longitude, ), ),
		]:
			try:
				value = bm_extract.as_string(d, k_from)
				if value:
					hd[k_to] = value
			except KeyError:
				pass

		for pd in bm_extract.as_list(d, "phoneNumbers"):
			number = bm_extract.as_string(pd, "number")
			if not number:
				continue

			type = bm_extract.as_string(pd, "type")

			if type in [ "main", "" ]:
				hd["%s.%s.%s" % ( vcard.TEL, vcard.Voice, vcard.Work, )] = number
			elif type in [ "fax", "data", ]:
				hd["%s.%s.%s" % ( vcard.TEL, vcard.Fax, vcard.Work, )] = number
			elif type == "mobile":
				hd["%s.%s.%s" % ( vcard.TEL, vcard.Mobile, vcard.Work, )] = number
			else:
				hd["%s.%s.%s" % ( vcard.TEL, vcard.Voice, vcard.Work, )] = number

		if hd:
			d["hcard:hcard"] = vcard.decompose(hd)

		#
		#
		#
		try:
			api.add_latlon(d, d.pop("lat"), d.pop("lng"), )
		except KeyError:
			pass

		#
		#	Remove stuff
		#
		for key in [ "country", "streetAddress", "city", "region", "staticMapUrl", "phoneNumbers", ]:
			try:
				del d[key]
			except KeyError:
				pass

		#
		#	The result
		#
		return	d

	
class VideoSearch(Google):
	_uri_base = "http://ajax.googleapis.com/ajax/services/search/video"
	_google_keys = google_keys_core + [
		"location",
	]

	def CustomizeAtomItem(self, d):
		d = Google.CustomizeAtomItem(self, d)

		mediad = {}

		try: d.pop('videoType')
		except: pass

		try:
			value = d.pop('duration')
			if value:
				mediad['@duration'] = bm_extract.coerce_int(value)
		except KeyError:
			pass

		try:
			value = d.pop('playUrl')
			if value:
				mediad['@url'] = value
		except KeyError:
			pass

		try:
			value = d.pop('tbUrl')
			if value:
				mediad['media:thumbnail'] = {
					"@url" : value
				}
		except KeyError:
			pass

		try:
			value = d.pop('tbHeight')
			if value:
				mediad['@height'] = bm_extract.coerce_int(value)
		except KeyError:
			pass

		try:
			value = d.pop('tbWidth')
			if value:
				mediad['@width'] = bm_extract.coerce_int(value)
		except KeyError:
			pass

		try:
			value = d.pop('rating')
			if value:
				mediad['media:rating'] = {
					"@" : bm_extract.coerce_float(value),
					"@scheme" : "urn:google",
				}
		except KeyError:
			pass

		if mediad:
			mediad["@type"] = "video/x-flv"
			mediad["@medium"] = "video"
			mediad["media:title"] = d.get("title")

			d["media:content"] = mediad

		return	d

class BlogSearch(Google):
	_uri_base = "http://ajax.googleapis.com/ajax/services/search/blogs"
	_atom_item = {
		"titleFormatted" : "title", 
		"title" : "titleNoFormatting", 
		"link" : "postUrl", 
	}
	
class NewsSearch(Google):
	_uri_base = "http://ajax.googleapis.com/ajax/services/search/news"

	_google_keys = google_keys_core + [
		"location",
		"image",
	]

	def CustomizeAtomItem(self, d):
		image = bm_extract.as_string(d, "image.tbUrl")
		width = bm_extract.as_int(d, "image.tbWidth")
		height = bm_extract.as_int(d, "image.tbHeight")
		
		if image:
			mediad = {
				"@url" : image,
				"@medium" : "image",
			}

			if width: mediad["@width"] = width
			if height: mediad["@height"] = width

			content_type, encoding = mimetypes.guess_type(image)
			if content_type:
				mediad["@type"] = content_type

			d["media:content"] = mediad
		
		return	Google.CustomizeAtomItem(self, d)
	
class BookSearch(Google):
	_uri_base = "http://ajax.googleapis.com/ajax/services/search/books"
	
	def CustomizeAtomItem(self, d):
		#
		#	Image
		#
		image = bm_extract.as_string(d, "tbUrl")
		width = bm_extract.as_int(d, "tbWidth")
		height = bm_extract.as_int(d, "tbHeight")

		api.remove_keys(d, [ "tbUrl", "tbWidth", "tbHeight", ])

		if image:
			mediad = {
				"@url" : image,
				"@medium" : "image",
			}

			if width: mediad["@width"] = width
			if height: mediad["@height"] = width

			content_type, encoding = mimetypes.guess_type(image)
			if content_type:
				mediad["@type"] = content_type

			d["media:content"] = mediad

		#
		#	hproduct
		#
		bookd = {}
		properties = {}
		identifiers = {}

		try:
			value = d.pop("bookId")
			if value and value.startswith("ISBN"):
				identifiers["isbn"] = value[4:]
		except KeyError:
			pass

		try:
			value = d.pop("pageCount")
			if value:
				properties["page-count"] = bm_extract.coerce_int(value)
		except KeyError:
			pass

		try:
			value = d.pop("publishedYear")
			if value:
				properties["published-year"] = bm_extract.coerce_int(value)
		except KeyError:
			pass

		if identifiers or properties or bookd:
			if identifiers:
				bookd["identifiers"] = identifiers

			if properties:
				bookd["properties"] = properties

			d["hproduct:book"] = bookd
		
		#
		#	Other stuff
		#
		return	Google.CustomizeAtomItem(self, d)

class ImageSearch(Google):
	_uri_base = "http://ajax.googleapis.com/ajax/services/search/images"

	def CustomizeAtomItem(self, d):
		d = Google.CustomizeAtomItem(self, d)

		mediad = {}

		#
		#	Image Thumbnail
		#
		image = bm_extract.as_string(d, "tbUrl")
		width = bm_extract.as_int(d, "tbWidth")
		height = bm_extract.as_int(d, "tbHeight")

		api.remove_keys(d, [ "tbUrl", "tbWidth", "tbHeight", ])

		if image:
			thumbd = {
				"@url" : image,
			}

			if width: thumbd["@width"] = width
			if height: thumbd["@height"] = width

			mediad["thumbnail"] = thumbd

		#
		#	Original Image
		#
		image = bm_extract.as_string(d, "link")
		width = bm_extract.as_int(d, "width")
		height = bm_extract.as_int(d, "height")

		api.remove_keys(d, [ "width", "height", ])

		if image:
			mediad["@url"] = image
			mediad["@medium"] = "image"

			if width: mediad["@width"] = width
			if height: mediad["@height"] = width

			content_type, encoding = mimetypes.guess_type(image)
			if content_type:
				mediad["@type"] = content_type

		if mediad:
			d["media:content"] = mediad

		return	d
	
class PatentSearch(Google):
	_uri_base = "http://ajax.googleapis.com/ajax/services/search/patent"
	
	def CustomizeAtomItem(self, d):
		#
		#	Image
		#
		image = bm_extract.as_string(d, "tbUrl")
		width = bm_extract.as_int(d, "tbWidth")
		height = bm_extract.as_int(d, "tbHeight")

		api.remove_keys(d, [ "tbUrl", "tbWidth", "tbHeight", ])

		if image:
			mediad = {
				"@url" : image,
				"@medium" : "image",
			}

			if width: mediad["@width"] = width
			if height: mediad["@height"] = width

			content_type, encoding = mimetypes.guess_type(image)
			if content_type:
				mediad["@type"] = content_type

			d["media:content"] = mediad

		#
		#	hproduct
		#
		patentd = {}
		properties = {}

		for ( k_from, k_to ) in [
			( "patentNumber", "patent-number", ),
			( "patentStatus", "patent-status", ),
			( "assignee", "assignee", ),
		]:
			try:
				value = d.pop(k_from)
				if value:
					properties[k_to] = value
			except KeyError:
				pass

		try:
			value = d.pop("applicationDate")
			if value:
				value = bm_extract.coerce_datetime(value, otherwise = None, atom = True)

			if value:
				properties["application-date"] = value
		except KeyError:
			pass

		if properties or patentd:
			if properties:
				patentd["properties"] = properties

			d["hproduct:patent"] = patentd

		#
		#	Other stuff
		#
		return	Google.CustomizeAtomItem(self, d)

if __name__ == '__main__':
	bm_cfg.cfg.initialize()

	## api_key = bm_cfg.cfg.as_string('google_maps.api_key')
	## referer = "http://code.davidjanes.com"
	query = "Paris Hilton"
	search = "web"

	query = "Bistro on Avenue"
	query = "CIBC"
	search = "local"

	Log.verbose = True

	apio.atom_like = True

	if search == "web":
		apio = WebSearch(q = query)
	elif search == "local":
		apio = LocalSearch(q = query, sll = "43.4,-79.23")
	elif search == "video":
		apio = VideoSearch(q = query)
	elif search == "blog":
		apio = BlogSearch(q = query)
	elif search == "news":
		apio = NewsSearch(q = query)
	elif search == "book":
		apio = BookSearch(q = query)
	elif search == "image":
		apio = ImageSearch(q = query)
	elif search == "patent":
		apio = PatentSearch(q = query)

	for item in apio.items:
		pprint.pprint(item)
